/*
 * Created on 2005-3-28
 *
 */
package com.hd123.rumba.catalog;

import java.util.Date;
import java.util.List;

import net.sf.hibernate.FlushMode;
import net.sf.hibernate.Hibernate;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.type.Type;

import com.hd123.rumba.domain.IDomain;
import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.exception.DuplicateIdException;
import com.hd123.rumba.exception.ParameterException;
import com.hd123.rumba.exception.StringTooLongException;
import com.hd123.rumba.log.EntityLogger;
import com.hd123.rumba.runtime.IBeforeRemove;
import com.hd123.rumba.runtime.Manager;
import com.hd123.rumba.runtime.NamingMgr;
import com.hd123.rumba.runtime.ObserverRegistry;
import com.hd123.rumba.runtime.OperateContext;
import com.hd123.rumba.sql.ResultPage;
import com.hd123.rumba.sql.SQLSubquery;
import com.hd123.rumba.user.IUser;
import com.hd123.rumba.user.IUserMgr;
import com.hd123.rumba.util.LevelCode;

/**
 * @author caili
 */
public class CatalogMgr extends Manager implements ICatalogMgr {

  public static final String CAPTION_MANAGER = "目录与分类管理员";

  private static final String EVENT_REMOVECATALOG = "删除目录";
  private static final String EVENT_REMOVECATEGORY = "删除类别";

  public CatalogMgr() {
    super();
    this.registryClass("com.hd123.rumba.catalog.ICatalog", "com.hd123.rumba.catalog.Catalog");
    this.registryClass("com.hd123.rumba.catalog.ICategory", "com.hd123.rumba.catalog.Category");
  }

  // //////////////////////////////
  //
  // Catalog
  //

  public LevelCode getLevelCode(ICatalog catalog) throws BusinessException, HibernateException {
    String rule = "";
    if (catalog.getLevel1Length() > 0)
      rule = rule + Integer.toString(catalog.getLevel1Length());
    if (catalog.getLevel2Length() > 0)
      rule = rule + Integer.toString(catalog.getLevel2Length());
    if (catalog.getLevel3Length() > 0)
      rule = rule + Integer.toString(catalog.getLevel3Length());
    if (catalog.getLevel4Length() > 0)
      rule = rule + Integer.toString(catalog.getLevel4Length());
    return new LevelCode(rule);
  }

  /**
   * 创建容纳itemType对象的目录. 如果是第一个这种对象类型的目录, 则自动设置成主目录.
   * 
   * @param domain
   * @param id
   * @param itemType
   * @return
   * @throws HibernateException
   */
  public ICatalog createCatalog(IDomain domain, String code, String itemType)
      throws HibernateException, BusinessException {
    this.checkParameterNotNull(domain, "domain");
    this.checkParameterNotNull(code, "code");
    this.checkParameterNotNull(itemType, "itemType");
    ICatalog catalog = getCatalogByCode(domain, code);
    if (catalog != null)
      throw new DuplicateIdException("域" + domain.getCode() + "中已经存在code为" + code + "的目录");

    catalog = new Catalog();
    catalog.setDomain(domain);
    catalog.setCode(code);
    catalog.setItemType(itemType);
    catalog.setIsMaster(createCatalogsList(domain, itemType).size() == 0 ? Boolean.TRUE
        : Boolean.FALSE);

    return catalog;
  }

  public ICatalog getCatalog(String uuid) throws BusinessException, HibernateException {
    ICatalog catalog = null;
    if (uuid != null)
      catalog = (ICatalog) getSession().get(Catalog.class, uuid);
    return catalog;
  }

  public ICatalog getCatalogByCode(IDomain domain, String code) throws BusinessException,
      HibernateException {
    this.checkParameterNotNull(domain, "domain");
    ICatalog catalog = null;
    if (code != null) {
      List list = getSession().find("from Catalog c where c.code = ? and c.domain.uuid=?",
          new Object[] {
              code.toUpperCase(), domain.getUuid() }, new Type[] {
              Hibernate.STRING, Hibernate.STRING });
      if (list.size() > 0) {
        catalog = (ICatalog) list.get(0);
      }
    }
    return catalog;
  }

  /**
   * 取得容纳ItemType对象的主目录. 如果不存在, 返回空.
   * 
   * @param domain
   * @param itemType
   * @return
   * @throws ParameterException
   * @throws HibernateException
   */
  public ICatalog getMasterCatalog(IDomain domain, String itemType) throws BusinessException,
      HibernateException {
    ICatalog catalog = null;
    List catalogs = createCatalogsList(domain, itemType);
    for (int i = 0; i < catalogs.size(); ++i) {
      ICatalog c = (ICatalog) catalogs.get(i);
      if (c.getIsMaster().booleanValue()) {
        catalog = c;
        break;
      }
    }
    return catalog;
  }

  /**
   * 取得域domain中容纳itemType对象的目录. 如果不存在, 返回的List.size()==0
   * 
   * @param domain
   * @param itemType
   * @return
   * @throws ParameterException
   * @throws HibernateException
   */
  public List createCatalogsList(IDomain domain, String itemType) throws BusinessException,
      HibernateException {
    checkParameterNotNull(domain, "domain");
    checkParameterNotNull(itemType, "itemType");
    return getSession().find(
        "from Catalog c where c.domain.uuid=? and c.itemType=? order by c.code", new Object[] {
            domain.getUuid(), itemType }, new Type[] {
            Hibernate.STRING, Hibernate.STRING });
  }

  /**
   * 取得域domain中所有的目录
   * 
   * @param domain
   * @param pageSize
   * @param page
   * @return
   * @throws Exception
   * @throws ParameterException
   */
  public ResultPage createAllCatalogsPage(IDomain domain, int pageSize, int page)
      throws HibernateException, BusinessException {
    if (domain == null) {
      throw new ParameterException("必须的参数domain不能为null");
    }
    SQLSubquery sq = new SQLSubquery();
    sq.addSelect("c");
    sq.addFrom("Catalog c");
    sq.addWhere("c.domain.uuid=?", domain.getUuid());
    sq.addOrder("c.code", "asc");
    return ResultPage.createHibernateResultPage(getSession(), sq, pageSize, page);
  }

  /** @deprecated */
  public void saveCatalog(ICatalog catalog) throws BusinessException, HibernateException {
    IUserMgr userMgr = (IUserMgr) NamingMgr.getInstance().lookupMgr(IUserMgr.class);
    saveCatalog(catalog, userMgr.getSysUser(), catalog.getOca());
  }

  /** @deprecated */
  public void saveCatalog(ICatalog catalog, IUser operator, long oca) throws BusinessException,
      HibernateException {
    this.saveCatalog(catalog, new OperateContext(operator));
  }

  public void saveCatalog(ICatalog catalog, OperateContext operate) throws BusinessException,
      HibernateException {
    this.checkParameterNotNull(catalog, "catalog");
    this.checkParameterNotNull(operate, "operate");

    IDomain domain = catalog.getDomain();
    if (catalog.getUuid() != null) {
      getSession().setFlushMode(FlushMode.COMMIT);
      ICatalog ecatalog = getCatalogByCode(domain, catalog.getCode());
      if (ecatalog != null && !ecatalog.equals(catalog))
        throw new DuplicateIdException("域" + domain.getCode() + "中已经存在code为" + ecatalog.getCode()
            + "的目录");
    }

    Date operTime = operate.getTime();
    EntityLogger logger = new EntityLogger(this);
    logger.setEntity(catalog);
    logger.setEntityConstClass(ICatalog.class);
    logger.setOperator(operate.getOperator());
    logger.setTime(operTime);

    boolean isNew = catalog.getUuid() == null;
    catalog.setLastModified(operTime);
    catalog.setLastModifier(operate.getOperator().getCodeName());
    getSession().saveOrUpdate(catalog);

    if (isNew)
      logger.log(EntityLogger.EVENT_ADDNEW, catalog.getState(), catalog.getName() + "["
          + catalog.getCode() + "]");
    else
      logger.log(EntityLogger.EVENT_MODIFY, catalog.getState(), catalog.getName() + "["
          + catalog.getCode() + "]");

    if (catalog.getIsMaster().booleanValue()) {
      List catalogs = createCatalogsList(domain, catalog.getItemType());
      for (int i = 0; i < catalogs.size(); ++i) {
        Catalog ecatalog = (Catalog) catalogs.get(i);
        if (ecatalog.isMaster().booleanValue() && !ecatalog.equals(catalog)) {
          ecatalog.setMaster(Boolean.FALSE);
          getSession().update(ecatalog);
          logger.setEntity(ecatalog);
          logger.log(EntityLogger.EVENT_MODIFY, ecatalog.getState(), null);
        }
      }
    }
  }

  /** @deprecated */
  public void removeCatalog(ICatalog catalog, IUser operator) throws BusinessException,
      HibernateException {
    this.removeCatalog(catalog, new OperateContext(operator));
  }

  public void removeCatalog(ICatalog catalog, OperateContext operate) throws BusinessException,
      HibernateException {
    if (catalog == null) {
      throw new ParameterException("必须的参数catalog不能为null");
    }
    if (catalog.getIsMaster().booleanValue()) {
      List catalogs = createCatalogsList(catalog.getDomain(), catalog.getItemType());
      if (catalogs.size() != 0) {
        throw new BusinessException("不能删除主目录.");
      }
    }
    ResultPage rp = createCatalogCategoriesPage(catalog, DIRECTCHILDREN, 0, 0);
    if (rp.getContentIterator().hasNext()) {
      throw new BusinessException("目录中有类别, 不能删除此目录");
    }
    EntityLogger logger = new EntityLogger(this);
    logger.setEntity(catalog);
    logger.setEntityConstClass(ICatalog.class);
    logger.setOperator(operate.getOperator());
    logger.setTime(operate.getTime());
    logger.log(EVENT_REMOVECATALOG, catalog.getState(), catalog.getName() + "[" + catalog.getCode()
        + "]");
    getSession().delete(catalog);
  }

  // /////////////////////////////////////
  //
  // Category
  //

  /**
   * 创建目录catalog中的类别. id需要满足此目录的类别级别要求. 如果id所表示的类别不是顶级类别, 则其父类别必须存在.
   * 
   * @param catalog
   * @param id
   * @return
   */
  public ICategory createCategory(ICatalog catalog, String code) throws ParameterException,
      BusinessException, HibernateException, DuplicateIdException, StringTooLongException {
    checkParameterNotNull(catalog, "catalog");
    checkParameterNotNull(code, "code");

    ICategory category = getCategoryByCode(catalog, code);
    if (category != null) {
      throw new DuplicateIdException(Category.CAPTION_CODE + code + "已经存在");
    }

    category = new Category();
    category.setDomain(catalog.getDomain());
    category.setCatalog(catalog);
    category.setCode(code);
    category.setParent(getParentCategoryByCode(catalog, code));
    return category;
  }

  public boolean isValidCategoryId(ICatalog catalog, String code) {
    boolean r = false;
    try {
      getParentCategoryByCode(catalog, code);
      r = true;
    } catch (Exception e) {
      r = false;
    }
    return r;
  }

  /**
   * 分析给定的子类别的code, 取得父类别
   * 
   * @param catalog
   * @param code
   *          子类别的code
   * @return 如果id表示的类别是顶级类别, 则返回null
   * @throws BusinessException:
   *           code表示的父类别不存在; code的长度非法
   * @throws ParameterException
   * @throws HibernateException
   */
  public ICategory getParentCategoryByCode(ICatalog catalog, String code) throws BusinessException,
      HibernateException {
    ICategory parent = null;
    String parentId = getParentCode(catalog, code);
    if (parentId != null) {
      parent = getCategoryByCode(catalog, parentId);
      if (parent == null)
        throw new BusinessException("父类别" + parentId + "不存在");
    }
    return parent;
  }

  /**
   * 分析给定的id, 取得父类别的id
   * 
   * @param catalog
   * @param id
   * @return
   * @throws BusinessException
   *           给定的id不是一个(长度上)合法的id
   */
  private String getParentCode(ICatalog catalog, String code) throws BusinessException {
    int i1 = catalog.getLevel1Length();
    int i2 = catalog.getLevel2Length() + i1;
    int i3 = catalog.getLevel3Length() + i2;
    int i4 = catalog.getLevel4Length() + i3;
    int codelen = code.length();
    String parentCode = null;
    if (codelen == i1)
      parentCode = null;
    else if (codelen == i2)
      parentCode = code.substring(0, i1);
    else if (codelen == i3)
      parentCode = code.substring(0, i2);
    else if (codelen == i4)
      parentCode = code.substring(0, i3);
    else
      throw new BusinessException(code + "不是一个(长度上)合法的类别code");
    return parentCode;
  }

  public ICategory getCategory(String uuid) throws HibernateException, BusinessException {
    Category category = null;
    if (uuid != null)
      category = (Category) getSession().get(Category.class, uuid);
    return category;
  }

  public ICategory getCategoryByCode(ICatalog catalog, String code) throws BusinessException,
      HibernateException {
    this.checkParameterNotNull(catalog, "catalog");
    Category category = null;
    if (code != null) {
      List list = getSession().find("from Category c where c.code = ? and c.catalog.uuid=?",
          new Object[] {
              code, catalog.getUuid() }, new Type[] {
              Hibernate.STRING, Hibernate.STRING });
      if (list.size() > 0) {
        category = (Category) list.get(0);
      }
    }
    return category;
  }

  /** @deprecated */
  public void saveCategory(ICategory category) throws HibernateException, BusinessException {
    IUserMgr userMgr = (IUserMgr) NamingMgr.getInstance().lookupMgr(IUserMgr.class);
    saveCategory(category, userMgr.getSysUser(), category.getOca());
  }

  /** @deprecated */
  public void saveCategory(ICategory category, IUser operator, long oca) throws HibernateException,
      BusinessException {
    this.saveCategory(category, new OperateContext(operator));
  }

  public void saveCategory(ICategory category, OperateContext operate) throws HibernateException,
      BusinessException {
    this.checkParameterNotNull(category, "category");
    this.checkParameterNotNull(operate, "operate");

    if (category.getCatalog() == null) {
      throw new ParameterException("category所属的目录不能是null");
    }

    if (!isValidCategoryId(category.getCatalog(), category.getCode())) {
      throw new BusinessException("id不合法: 长度不正确或父类不存在.");
    }

    if (category.getUuid() != null) {
      getSession().setFlushMode(FlushMode.COMMIT);
      ICategory ecategory = getCategoryByCode(category.getCatalog(), category.getCode());
      if (ecategory != null && !ecategory.equals(category))
        throw new DuplicateIdException("目录" + category.getCatalog().getCode() + "中已经存在ID为"
            + category.getCode() + "的类别");
    }

    Date operTime = operate.getTime();
    EntityLogger logger = new EntityLogger(this);
    logger.setEntity(category);
    logger.setEntityConstClass(ICategory.class);
    logger.setOperator(operate.getOperator());
    logger.setTime(operTime);

    boolean isNew = category.getUuid() == null;
    category.setParent(getParentCategoryByCode(category.getCatalog(), category.getCode()));
    category.setLastModified(operTime);
    category.setLastModifier(operate.getOperator().getCodeName());
    getSession().saveOrUpdate(category);
    if (isNew)
      logger.log(EntityLogger.EVENT_ADDNEW, category.getState(), category.getNameCode());
    else
      logger.log(EntityLogger.EVENT_MODIFY, category.getState(), category.getNameCode());
  }

  /** @deprecated */
  public void removeCategory(ICategory category, IUser operator) throws HibernateException,
      BusinessException {
    this.removeCategory(category, new OperateContext(operator));
  }

  public void removeCategory(ICategory category, OperateContext operate) throws HibernateException,
      BusinessException {
    this.checkParameterNotNull(category, "category");
    this.checkParameterNotNull(operate, "operate");
    if (category == null) {
      throw new ParameterException("必须的参数category不能是null");
    }

    ResultPage rp = createChildCategoriesPage(category, DIRECTCHILDREN, 0, 0);
    if (rp.getElementCount() > 0) {
      throw new BusinessException("类别" + category.getCode() + "有" + rp.getElementCount()
          + "个子类, 不能删除");
    }
    ObserverRegistry.notifyObservers(IBeforeRemove.class, category);

    EntityLogger logger = new EntityLogger(this);
    logger.setEntity(category);
    logger.setEntityConstClass(ICategory.class);
    logger.setOperator(operate.getOperator());
    logger.setTime(operate.getTime());
    logger.log(EVENT_REMOVECATEGORY, category.getState(), category.getNameCode());
    getSession().delete(category);
  }

  /**
   * 取得该目录中的类别, 按类别Id的升序排列. <br>
   * 如果该目录没有类别, 返回的List.size() == 0.
   * 
   * @param mode:
   *          ALLCHILDREN=所有类别, DIRECTCHILDREN=顶级类别
   * @param pageSize:
   *          每页包含的记录数. 0=全部记录.
   * @param page:
   *          第几页.
   */
  public ResultPage createCatalogCategoriesPage(ICatalog catalog, int mode, int pageSize, int page)
      throws BusinessException, HibernateException {
    if (catalog == null) {
      throw new ParameterException("必须的参数catalog不能为null");
    }
    SQLSubquery sq = new SQLSubquery();
    sq.addSelect("c");
    sq.addFrom("Category c");
    sq.addWhere("c.catalog.uuid=?", catalog.getUuid());
    if (mode == DIRECTCHILDREN)
      sq.addWhere("and c.parent is null");
    sq.addOrder("c.code");
    return ResultPage.createHibernateResultPage(getSession(), sq, pageSize, page);
  }

  /**
   * 取得子类别. 按子类别的Id升序排列. 如果没有子类别, 返回的List.size()==0.
   * 
   * @param mode:
   *          ALLCHILDREN = 所有子类别, DIRECTCHILDREN=直接的子类别
   * @param pageSize:
   *          每页包含的记录数. 0=全部记录.
   * @param page:
   *          第几页.
   * @return
   */
  public ResultPage createChildCategoriesPage(ICategory category, int mode, int pageSize, int page)
      throws BusinessException, HibernateException {
    this.checkParameterNotNull(category, "category");
    SQLSubquery sq = new SQLSubquery();
    sq.addSelect("c");
    sq.addFrom("Category c");
    sq.addWhere("c.catalog.uuid=?", category.getCatalog().getUuid());
    if (mode == DIRECTCHILDREN)
      sq.addWhere("and c.parent.uuid=?", category.getUuid());
    else
      sq.addWhere("and c.code like ?", category.getCode(), SQLSubquery.WC_RIGHT);
    sq.addOrder("c.code");
    return ResultPage.createHibernateResultPage(getSession(), sq, pageSize, page);
  }

  public boolean hasChildren(ICategory category) throws BusinessException, HibernateException {
    ResultPage rp = createChildCategoriesPage(category, DIRECTCHILDREN, 0, 0);
    return rp.getElementCount() > 0;
  }

  /**
   * 是否是最后一级的类别。根据catalog的level长度进行判断。
   * 
   * @param category:
   *          需要检查的类别
   * @return
   */
  public boolean isLastChildren(ICategory category) throws BusinessException, HibernateException {
    return category.getCode().length() == category.getCatalog().getLevel1Length()
        + category.getCatalog().getLevel2Length() + category.getCatalog().getLevel3Length()
        + category.getCatalog().getLevel4Length();

  }

}
